
// Copyright (c) WanSheng Intelligent Corp. All rights reserved.
// Licensed under the LGPL v3.0 license. 

#include "modbus_plugin.h"

i_modbus_t *g_modbus = NULL;
pthread_mutex_t g_device_list_mutex;
i_modbus_device_t* g_devices = NULL;


void init_plugin()
{
    pthread_mutex_init(&g_device_list_mutex, NULL);
}

void uninit_plugin()
{
    pthread_mutex_lock(&g_device_list_mutex);
    i_modbus_device_t* device = g_devices;
    while (device != NULL)
    {
        i_modbus_device_t*  current = device;
        device = device->next;
        free(current->id);
        free(current);
    }
    g_devices = NULL;

    i_modbus_t *bus = g_modbus;
    while (bus != NULL)
    {
        i_modbus_t * current = bus;
        bus = bus->next;
        free(current);
    }
    g_modbus = NULL;

    pthread_mutex_unlock(&g_device_list_mutex);
    pthread_mutex_destroy(&g_device_list_mutex);

    if(g_devices_info)
    {
        wa_plugin_free_device_info(g_devices_info, g_devices_info_size);
        g_devices_info = NULL;
        g_devices_info_size = 0;
    }
}



i_modbus_t * find_modbus_by_name(const char* name)
{
    i_modbus_t *bus = g_modbus;
    while (bus != NULL)
    {
        if ((strcmp(bus->addr->bus_name, name) == 0))
            return bus;

        bus = bus->next;
    }

    return NULL;

}


i_modbus_t * find_modbus_by_info(wa_bus_info_t *info)
{
    i_modbus_t *bus = g_modbus;

    while (bus != NULL)
    {
        if (wa_bus_compare_info((bus->addr), info))
        {
            return bus;
        }

        bus = bus->next;
    }

    return NULL;
}


void add_modbus(i_modbus_t *modbus)
{
    if(g_modbus == NULL)
    {
        g_modbus = modbus;
        modbus->next = NULL;
    }
    else
    {
        modbus->next = g_modbus;
        g_modbus = modbus;
    }
}

const char * bus_name(i_modbus_t*  bus)
{
    return bus->addr->bus_name;
}

void set_modbus_connection_status(i_modbus_t*  bus, int status)
{
    // don't track the status change for the S_Bus_Closed
    if(status == S_Bus_Disconnected || 
        (bus->status != S_Bus_Closed && S_Bus_Closed != status))
    {

        WARNING("bus become %s,  name %s", S_Bus_Connected==status?"connected":"disconnected",
            bus_name(bus));
    }
    else
    {
        TraceV(FLAG_BUS_AUTO_CLOSE, "bus become %s,  name %s", 
            S_Bus_Closed==status?"closed":"connected",
            bus_name(bus));
    }

    bus->status = status;
}


bool retry_modbus_connect(i_modbus_t*  bus)
{
    if(modbus_connect(bus->bus_ctx) != -1)
    {
        set_modbus_connection_status(bus, S_Bus_Connected);
        return true;
    }
    return false;
}



void disconnet_modbus(i_modbus_t*  bus)
{
    WARNING("Disconnecting bus [%s]", SAFE_STRING(bus_name(bus)));
    modbus_close(bus->bus_ctx);
    set_modbus_connection_status(bus, S_Bus_Disconnected);
}



bool check_bus_connect(i_modbus_t * bus)
{
    if(bus->status == S_Bus_Disconnected ||
          bus->status == S_Bus_Closed)
    {
        if(retry_modbus_connect (bus))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    return true;
}


i_modbus_device_t* find_mb_device(const char* name)
{
    pthread_mutex_lock(&g_device_list_mutex);
    i_modbus_device_t* device = g_devices;

    while (device != NULL)
    {
        if (strcmp(device->id, name) == 0)
        {
            pthread_mutex_unlock(&g_device_list_mutex);
            return device;
        }

        device = device->next;
    }

    pthread_mutex_unlock(&g_device_list_mutex);
    return NULL;
}

void add_device(i_modbus_device_t* device)
{
    pthread_mutex_lock(&g_device_list_mutex);

    if (g_devices == NULL)
    {
        g_devices = device;
        device->next = NULL;
    }
    else
    {
        device->next = g_devices;
        g_devices = device;
    }

    pthread_mutex_unlock(&g_device_list_mutex);
}


int task_bus_connect(wa_task_t task)
{
    pthread_mutex_lock(&g_device_list_mutex);

    i_modbus_t *bus = g_modbus;
    while (bus != NULL)
    {
        check_bus_connect(bus);
        bus = bus->next;
    }

    pthread_mutex_unlock(&g_device_list_mutex);
}